TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import type { Metadata } from 'next';2import { notFound } from 'next/navigation';3import { PageHeader } from '@/components/ui/page-header';4import { Stat } from '@/components/ui/primitives';5import { AssetBrowser } from '@/components/market/asset-browser';6import { Suspense } from 'react';7import { Skeleton } from '@/components/ui/primitives';8import { getSet, getSetStats } from '@/lib/queries/markets';9import { catName, categoryCrumbs } from '@/lib/taxonomy';10import { fmtMoney, fmtNum, fmtRelative } from '@/lib/format';11import type { SP } from '@/lib/search-params';1213export const revalidate = 300;1415export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {16 const { slug } = await params;17 const s = await getSet(slug);18 if (!s) return { title: 'Set not found' };19 return { title: `${s.name} · ${catName(s.categorySlug)} set`, description: `${s.name}${s.releaseYear ? ` (${s.releaseYear})` : ''}: every tracked asset with valuation, sales and listings.` };20}2122export default async function SetPage({ params, searchParams }: { params: Promise<{ slug: string }>; searchParams: Promise<SP> }) {23 const { slug } = await params;24 const sp = await searchParams;25 const set = await getSet(slug);26 if (!set) notFound();27 const stats = await getSetStats(slug);28 return (29 <div>30 <PageHeader31 crumbs={[{ label: 'Markets', href: '/markets' }, ...categoryCrumbs(set.categorySlug), { label: set.name }]}32 title={set.name}33 description={`${catName(set.categorySlug)} set${set.code ? ` · code ${set.code}` : ''}${set.releaseDate ? ` · released ${set.releaseDate}` : set.releaseYear ? ` · ${set.releaseYear}` : ''}${set.totalItems ? ` · ${set.totalItems} items in the official set` : ''}${set.language ? ` · ${set.language.toUpperCase()}` : ''}.`}34 />35 <section className="grid grid-cols-2 gap-px overflow-hidden rounded-md border border-border bg-border sm:grid-cols-4 lg:grid-cols-7">36 {[37 ['Tracked assets', fmtNum(stats.assets)],38 ['With valuation', fmtNum(stats.priced)],39 ['Sales', fmtNum(stats.sales)],40 ['Active listings', fmtNum(stats.listings)],41 ['Median RIV', stats.medianRivUsd != null ? fmtMoney(stats.medianRivUsd) : '—'],42 ['Top RIV', stats.maxRivUsd != null ? fmtMoney(stats.maxRivUsd) : '—'],43 ['Last sale', stats.lastSaleAt ? fmtRelative(stats.lastSaleAt) : '—'],44 ].map(([k, v]) => (45 <div key={k} className="bg-elevated px-3 py-2.5">46 <Stat label={k} value={v} />47 </div>48 ))}49 </section>50 {stats.sumRivUsd != null && stats.priced === stats.assets && stats.assets > 0 ? <p className="mt-1 text-[11px] text-subtle">Sum of RIV across all {stats.assets} priced assets: {fmtMoney(stats.sumRivUsd)} (a complete-set value indication, not a market cap).</p> : null}51 <div className="mt-4">52 <Suspense fallback={<Skeleton className="h-96" />}>53 <AssetBrowser scope={{ set: slug }} sp={{ ...sp, sort: sp.sort ?? 'number' }} basePath={`/set/${slug}`} title={`Assets in ${set.name}`} pageSize={60} />54 </Suspense>55 </div>56 </div>57 );58}59